#!/bin/bash

# Vue Admin System 部署脚本
# 支持开发、测试、生产环境的自动化部署

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 配置变量
PROJECT_NAME="vue-admin-system"
DEPLOY_USER="deploy"
BACKUP_DIR="/var/backups/${PROJECT_NAME}"
LOG_FILE="/var/log/${PROJECT_NAME}/deploy.log"

# 日志函数
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}

# 检查环境
check_environment() {
    local env=$1
    
    case $env in
        development|dev)
            DEPLOY_PATH="/var/www/${PROJECT_NAME}-dev"
            PM2_APP_NAME="${PROJECT_NAME}-dev"
            PORT=3001
            ;;
        staging|test)
            DEPLOY_PATH="/var/www/${PROJECT_NAME}-staging"
            PM2_APP_NAME="${PROJECT_NAME}-staging"
            PORT=3002
            ;;
        production|prod)
            DEPLOY_PATH="/var/www/${PROJECT_NAME}"
            PM2_APP_NAME="${PROJECT_NAME}"
            PORT=3000
            ;;
        *)
            log_error "不支持的环境: $env"
            exit 1
            ;;
    esac
    
    log_info "部署环境: $env"
    log_info "部署路径: $DEPLOY_PATH"
}

# 检查必要的命令
check_commands() {
    local commands=("node" "npm" "git" "pm2")
    
    for cmd in "${commands[@]}"; do
        if ! command -v $cmd &> /dev/null; then
            log_error "$cmd 未安装"
            exit 1
        fi
    done
    
    log_success "命令检查通过"
}

# 创建备份
create_backup() {
    if [ -d "$DEPLOY_PATH" ]; then
        local backup_name="${PROJECT_NAME}_$(date +%Y%m%d_%H%M%S)"
        local backup_path="${BACKUP_DIR}/${backup_name}"
        
        log_info "创建备份: $backup_path"
        
        mkdir -p "$BACKUP_DIR"
        cp -r "$DEPLOY_PATH" "$backup_path"
        
        # 保留最近 5 个备份
        cd "$BACKUP_DIR"
        ls -t | tail -n +6 | xargs -r rm -rf
        
        log_success "备份创建完成"
    fi
}

# 拉取代码
pull_code() {
    local branch=${1:-main}
    
    if [ -d "$DEPLOY_PATH" ]; then
        log_info "更新代码..."
        cd "$DEPLOY_PATH"
        git fetch origin
        git reset --hard origin/$branch
    else
        log_info "克隆代码..."
        git clone -b $branch "$GIT_REPO" "$DEPLOY_PATH"
        cd "$DEPLOY_PATH"
    fi
    
    log_success "代码更新完成"
}

# 安装依赖
install_dependencies() {
    log_info "安装前端依赖..."
    cd "$DEPLOY_PATH/apps/frontend"
    npm ci --production
    
    log_info "安装后端依赖..."
    cd "$DEPLOY_PATH/apps/backend"
    npm ci --production
    
    log_success "依赖安装完成"
}

# 构建项目
build_project() {
    log_info "构建前端项目..."
    cd "$DEPLOY_PATH/apps/frontend"
    npm run build
    
    log_info "构建后端项目..."
    cd "$DEPLOY_PATH/apps/backend"
    npm run build
    
    log_success "项目构建完成"
}

# 配置环境变量
setup_environment() {
    local env=$1
    local env_file="$DEPLOY_PATH/apps/backend/.env"
    
    log_info "配置环境变量..."
    
    case $env in
        development|dev)
            cat > "$env_file" << EOF
NODE_ENV=development
PORT=$PORT
DB_TYPE=sqlite
DB_DATABASE=./database.sqlite
JWT_SECRET=dev-secret-$(date +%s)
JWT_EXPIRES_IN=7d
LOG_LEVEL=debug
EOF
            ;;
        staging|test)
            cat > "$env_file" << EOF
NODE_ENV=staging
PORT=$PORT
DB_TYPE=sqlite
DB_DATABASE=./staging.sqlite
JWT_SECRET=staging-secret-$(date +%s)
JWT_EXPIRES_IN=1h
LOG_LEVEL=info
EOF
            ;;
        production|prod)
            # 生产环境需要手动配置敏感信息
            if [ ! -f "$env_file" ]; then
                cat > "$env_file" << EOF
NODE_ENV=production
PORT=$PORT
DB_TYPE=mysql
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=vue_admin
DB_PASSWORD=CHANGE_ME
DB_DATABASE=vue_admin
JWT_SECRET=CHANGE_ME_TO_SECURE_SECRET
JWT_EXPIRES_IN=7d
LOG_LEVEL=info
EOF
                log_warning "请手动配置生产环境变量: $env_file"
            fi
            ;;
    esac
    
    log_success "环境变量配置完成"
}

# 数据库迁移
migrate_database() {
    log_info "执行数据库迁移..."
    
    cd "$DEPLOY_PATH/apps/backend"
    
    if npm run --silent 2>/dev/null | grep -q "migration:run"; then
        npm run migration:run
        log_success "数据库迁移完成"
    else
        log_warning "未找到数据库迁移脚本"
    fi
}

# 运行测试
run_tests() {
    log_info "运行测试..."
    
    # 前端测试
    cd "$DEPLOY_PATH/apps/frontend"
    if npm run --silent 2>/dev/null | grep -q "test"; then
        npm run test
        log_success "前端测试通过"
    fi
    
    # 后端测试
    cd "$DEPLOY_PATH/apps/backend"
    if npm run --silent 2>/dev/null | grep -q "test"; then
        npm run test
        log_success "后端测试通过"
    fi
}

# 启动应用
start_application() {
    log_info "启动应用..."
    
    cd "$DEPLOY_PATH/apps/backend"
    
    # 停止现有应用
    if pm2 list | grep -q "$PM2_APP_NAME"; then
        pm2 stop "$PM2_APP_NAME"
        pm2 delete "$PM2_APP_NAME"
    fi
    
    # 启动新应用
    pm2 start dist/main.js --name "$PM2_APP_NAME" --env production
    pm2 save
    
    log_success "应用启动完成"
}

# 健康检查
health_check() {
    local max_attempts=30
    local attempt=1
    
    log_info "执行健康检查..."
    
    while [ $attempt -le $max_attempts ]; do
        if curl -f -s "http://localhost:$PORT/api/health" > /dev/null; then
            log_success "健康检查通过"
            return 0
        fi
        
        log_info "等待应用启动... ($attempt/$max_attempts)"
        sleep 2
        ((attempt++))
    done
    
    log_error "健康检查失败"
    return 1
}

# 回滚部署
rollback() {
    log_warning "开始回滚..."
    
    # 停止当前应用
    if pm2 list | grep -q "$PM2_APP_NAME"; then
        pm2 stop "$PM2_APP_NAME"
        pm2 delete "$PM2_APP_NAME"
    fi
    
    # 恢复备份
    local latest_backup=$(ls -t "$BACKUP_DIR" | head -n 1)
    if [ -n "$latest_backup" ]; then
        rm -rf "$DEPLOY_PATH"
        cp -r "$BACKUP_DIR/$latest_backup" "$DEPLOY_PATH"
        
        # 重新启动应用
        cd "$DEPLOY_PATH/apps/backend"
        pm2 start dist/main.js --name "$PM2_APP_NAME" --env production
        pm2 save
        
        log_success "回滚完成"
    else
        log_error "未找到备份文件"
        exit 1
    fi
}

# 清理旧文件
cleanup() {
    log_info "清理临时文件..."
    
    # 清理 npm 缓存
    npm cache clean --force
    
    # 清理 PM2 日志
    pm2 flush
    
    log_success "清理完成"
}

# 发送通知
send_notification() {
    local status=$1
    local env=$2
    
    # 这里可以集成 Slack、钉钉等通知服务
    log_info "部署通知: $status - $env 环境"
}

# 显示部署状态
show_status() {
    echo
    log_info "部署状态:"
    echo "  环境: $ENVIRONMENT"
    echo "  路径: $DEPLOY_PATH"
    echo "  端口: $PORT"
    echo "  PM2 应用: $PM2_APP_NAME"
    echo
    
    # 显示 PM2 状态
    pm2 list | grep "$PM2_APP_NAME" || true
    
    # 显示应用版本
    if [ -f "$DEPLOY_PATH/package.json" ]; then
        local version=$(grep '"version"' "$DEPLOY_PATH/package.json" | cut -d'"' -f4)
        echo "  版本: $version"
    fi
    
    echo
}

# 主部署函数
deploy() {
    local env=$1
    local branch=${2:-main}
    local skip_tests=${3:-false}
    
    log_info "开始部署 $env 环境..."
    
    # 创建日志目录
    mkdir -p "$(dirname "$LOG_FILE")"
    
    # 检查环境
    check_environment "$env"
    check_commands
    
    # 创建备份
    create_backup
    
    # 部署流程
    if pull_code "$branch" && \
       install_dependencies && \
       build_project && \
       setup_environment "$env" && \
       migrate_database; then
        
        # 可选运行测试
        if [ "$skip_tests" != "true" ]; then
            if ! run_tests; then
                log_error "测试失败，是否继续部署？(y/N)"
                read -r response
                if [[ ! "$response" =~ ^[Yy]$ ]]; then
                    log_error "部署中止"
                    exit 1
                fi
            fi
        fi
        
        # 启动应用
        if start_application && health_check; then
            cleanup
            send_notification "SUCCESS" "$env"
            log_success "部署成功完成！"
            show_status
        else
            log_error "应用启动失败，开始回滚..."
            rollback
            send_notification "FAILED" "$env"
            exit 1
        fi
    else
        log_error "部署过程中出现错误"
        send_notification "FAILED" "$env"
        exit 1
    fi
}

# 显示帮助信息
show_help() {
    echo "Vue Admin System 部署脚本"
    echo
    echo "用法:"
    echo "  $0 <环境> [分支] [选项]"
    echo
    echo "环境:"
    echo "  dev|development    开发环境"
    echo "  test|staging       测试环境"
    echo "  prod|production    生产环境"
    echo
    echo "选项:"
    echo "  --skip-tests       跳过测试"
    echo "  --rollback         回滚到上一个版本"
    echo "  --status           显示部署状态"
    echo "  --help             显示此帮助信息"
    echo
    echo "环境变量:"
    echo "  GIT_REPO           Git 仓库地址"
    echo
    echo "示例:"
    echo "  $0 dev                    # 部署开发环境"
    echo "  $0 prod main              # 部署生产环境的 main 分支"
    echo "  $0 test --skip-tests      # 部署测试环境并跳过测试"
    echo "  $0 prod --rollback        # 回滚生产环境"
    echo "  $0 --status               # 显示部署状态"
}

# 主程序入口
main() {
    case "$1" in
        --help)
            show_help
            exit 0
            ;;
        --status)
            if [ -n "$2" ]; then
                check_environment "$2"
                show_status
            else
                echo "请指定环境"
                exit 1
            fi
            ;;
        --rollback)
            if [ -n "$2" ]; then
                check_environment "$2"
                rollback
            else
                echo "请指定环境"
                exit 1
            fi
            ;;
        dev|development|test|staging|prod|production)
            ENVIRONMENT=$1
            BRANCH=${2:-main}
            SKIP_TESTS=false
            
            # 检查是否跳过测试
            if [[ "$*" == *"--skip-tests"* ]]; then
                SKIP_TESTS=true
            fi
            
            # 检查 Git 仓库地址
            if [ -z "$GIT_REPO" ]; then
                log_error "请设置 GIT_REPO 环境变量"
                exit 1
            fi
            
            deploy "$ENVIRONMENT" "$BRANCH" "$SKIP_TESTS"
            ;;
        "")
            log_error "请指定部署环境"
            show_help
            exit 1
            ;;
        *)
            log_error "未知选项: $1"
            show_help
            exit 1
            ;;
    esac
}

# 执行主程序
main "$@"